/*
 * LibMediaDecoder.h
 *
 *  Created on: 2018年7月23日
 *      Author: terry
 */

#ifndef LIBMEDIADECODER_H_
#define LIBMEDIADECODER_H_

///////////////////////////////////////////////////////////////////
#ifdef WIN32

    #ifndef NOMINMAX
    #define NOMINMAX
    #endif //NOMINMAX

    //#pragma warning( disable : 4786 )
    #include <winsock2.h>
    //#include <windows.h>

#else


#endif //WIN32


///////////////////////////////////////////////////////////////////
#include <stddef.h>
#include <string.h>
///////////////////////////////////////////////////////////////////

#ifndef SOCKET_T
    #ifdef WIN32
        typedef SOCKET  socket_t;
    #else
        typedef int     socket_t;
    #endif // WIN32

    #define SOCKET_T

#endif //SOCKET_T



#ifdef _MSC_VER
#if (_MSC_VER >= 1700)
    #include <stdint.h>
#else
    typedef signed char     int8_t;
    typedef unsigned char   uint8_t;
    typedef short           int16_t;
    typedef unsigned short  uint16_t;
    typedef int             int32_t;
    typedef unsigned        uint32_t;
    typedef long long       int64_t;
    typedef unsigned long long   uint64_t;
#endif

#else
    #include <stdint.h>
#endif //_MSC_VER


///////////////////////////////////////////////////////////////////
#ifdef WIN32
    #ifndef DLLEXPORT
    #define DLLEXPORT __declspec(dllexport)
    #endif //DLLEXPORT
#else
    #ifndef DLLEXPORT
    #define DLLEXPORT __attribute__ ((visibility ("default")))
    #endif //DLLEXPORT
#endif //WIN32

///////////////////////////////////////////////////////////////////

#ifdef WIN32
#else
    typedef void*   HANDLE;
    typedef int     BOOL;
    typedef void*	HWND;
    typedef void*	HDC;

	#define WINAPI

#endif //


/////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C"
{
#endif

/////////////////////////////////////////////////////////////////////////////

#ifndef MKTAG
#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))
#endif //MKTAG


typedef enum MDecode_MediaType
{
	MDECODE_MEDIA_NONE  = -1,
	MDECODE_MEDIA_VIDEO = 0,	/// 视频流.
	MDECODE_MEDIA_AUDIO,		/// 音频流.
	MDECODE_MEDIA_DATA,		/// 混合流.
	MDECODE_MEDIA_SUBTITLE
} MDecode_MediaType;


typedef enum MDecode_CodecType
{
	MDECODE_CODEC_NONE = 0,

	MDECODE_CODEC_MPEG2 = 2,
	MDECODE_CODEC_MPEG4 = 13,

	MDECODE_CODEC_H264 = 28,
	MDECODE_CODEC_HEVC = 174,
	MDECODE_CODEC_H265 = MDECODE_CODEC_HEVC,

	MDECODE_CODEC_G711U = 65542,
	MDECODE_CODEC_G711A,

	MDECODE_CODEC_MP3 = 0x15001,
	MDECODE_CODEC_AAC = 0X15002,

	MDECODE_CODEC_PS = MKTAG('M', 'P', '2', 'P'),	/// PS stream

	MDECODE_CODEC_RAW = 0X10101010
} MDecode_CodecType;


typedef enum MDecode_EventType
{
	MDECODE_EVENT_OPENING = 0X0100,	/// 打开文件中.
	MDECODE_EVENT_OPEN_FAILED,		/// 打开失败.
	MDECODE_EVENT_OPENED,			/// 打开成功.
	MDECODE_EVENT_CLOSED,			/// 文件关闭.

	MDECODE_EVENT_END,		/// 播放结束.

	MDECODE_EVENT_FORMAT_READY,	/// 已知媒体格式.

	MDECODE_EVENT_SEEKING,	/// 拖拽中.
	MDECODE_EVENT_SEEKED,		/// 拖拽完成.

	MDECODE_EVENT_FULLSCREEN,	/// 全屏变化.
	MDECODE_EVENT_VOLUME,		/// 音量变化.
	MDECODE_EVENT_MUTE,		/// 静音变化.
	MDECODE_EVENT_SCALE,		/// 播放速度变化.

	MDECODE_EVENT_PLAYING,		/// 播放中.
	MDECODE_EVENT_PAUSED,			/// 暂停.
	MDECODE_EVENT_STOPPED,		/// 停止播放.

	MDECODE_EVENT_MOUSE_DOWN = 0x1000,	/// 鼠标单击, 事件参数值是 int64_t, 低32位是x坐标, 高位是y坐标
	MDECODE_EVENT_MOUSE_UP,				/// 鼠标抬起
	MDECODE_EVENT_MOUSE_DOUBLE_DOWN,	/// 鼠标双击
	MDECODE_EVENT_MOUSE_ENTER,		/// 鼠标进入.
	MDECODE_EVENT_MOUSE_LEAVE,		/// 鼠标离开.

	MDECODE_EVENT_RETRY = 0x2000,
	MDECODE_EVENT_FIRST_VIDEO_FRAME,

} MDecode_EventType;


typedef enum MDecode_DrawGraphMode
{
	MDECODE_DRAW_GRAPH_NONE = 0,	/// 禁止绘制图形
	MDECODE_DRAW_GRAPH_RECT,		/// 绘制矩形
	MDECODE_DRAW_GRAPH_POLYGON		/// 绘制多边形

} MDecode_DrawGraphMode;

/**
 * 媒体格式.
 */
typedef struct MDecode_Format
{
	int	codec;		/// 视频编码(或者混合编码).

	int audioCodec;	/// 音频编码.
	int	channels;	/// 通道数.
	int samplerate;	/// 采样率.

	uint8_t* prop;
	int propSize;

	int configSize;      ///音频描述信息长度，即audioProp实际长度.
	uint8_t* config; ///音频描述信息.

} MDecode_Format;


typedef enum MDecode_MediaFlag
{
	MDECODE_FLAG_KEY = 0x01		/// 关键帧标识.

} MDecode_MediaFlag;


/// pixel format
typedef enum MDecode_PixelFormat
{
	MDECODE_PIXEL_NONE = -1,
    
	MDECODE_PIXEL_I420  = 0,  /* yuv 4:2:0 planar YUV420P */
	MDECODE_PIXEL_RGB24 = 2,    /// RGB24
	MDECODE_PIXEL_BGR24,	/// BGR24

	MDECODE_PIXEL_NV12 = 25,  /* yuv 4:2:0, with one y plane and one packed u+v */

	MDECODE_PIXEL_RGBA = 28,
	MDECODE_PIXEL_BGRA = 30,

} MDecode_PixelFormat;


/**
 * 音频采样格式.
 */
typedef enum MDecode_SampleFormat {
    MDECODE_SAMPLE_NONE = -1,//!< MDECODE_SAMPLE_NONE
    MDECODE_SAMPLE_U8,          ///< unsigned 8 bits
    MDECODE_SAMPLE_S16,         ///< signed 16 bits
    MDECODE_SAMPLE_S32,         ///< signed 32 bits
    MDECODE_SAMPLE_FLT,         ///< float
    MDECODE_SAMPLE_DBL,         ///< double

    MDECODE_SAMPLE_U8P,         ///< unsigned 8 bits, planar
    MDECODE_SAMPLE_S16P,        ///< signed 16 bits, planar
    MDECODE_SAMPLE_S32P,        ///< signed 32 bits, planar
    MDECODE_SAMPLE_FLTP,        ///< float, planar
    MDECODE_SAMPLE_DBLP,        ///< double, planar

    MDECODE_SAMPLE_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
} MDecode_SampleFormat;


typedef enum MDecode_Const
{
	MDECODE_MAX_PLANE = 8	/// 最大平面数.
} MDecode_Const;

/// 音视频帧.
typedef struct MDecode_Frame
{
	int type;	///@see MDecode_MediaType, 用于区分音频/视频.
	int fmt;	/// 帧格式, @see MDecode_PixelFormat. 对于音频, @see MDecode_SampleFormat

	unsigned char* data[MDECODE_MAX_PLANE];	/// 媒体数据, 对于视频, 每个元素指向一个平面;对于音频, 元素指向通道.
	int linesize[MDECODE_MAX_PLANE];		/// 第几个平面|通道的字节数.

	int64_t pts;	/// 时间戳.
	int flags;		/// 标记.

	/// for video only
	int width;		/// 视频高.
	int height;		/// 视频宽.

	/// for audio only
	int channels;	/// 音频通道.
	int sampleRate;	/// 采样率.
	int samples;	/// 采样数.

} MDecode_Frame;


/// 显示事件, 包括鼠标事件.
typedef struct MDecode_RenderEvent
{
	int event;
	int x;
	int y;
	int which;
	int modifier;
} MDecode_RenderEvent;


/// 鼠标键序号
typedef enum MDecode_MouseButton
{
	MDECODE_BUTTON_LEFT = 1,
	MDECODE_BUTTON_MIDDLE = 2,
	MDECODE_BUTTON_RIGHT = 3
} MDecode_MouseButton;

/////////////////////////////////////////////////////////////////////////////

/**
 * 初始化解码库.
 * @return TRUE 如果成功
 */
DLLEXPORT BOOL WINAPI MDecode_Init();

/**
 * 清理解码库.
 */
DLLEXPORT void WINAPI MDecode_Uninit();

/**
 * 获取版本.
 * @return 版本
 */
DLLEXPORT const char* WINAPI MDecode_GetVersion();


/**
 * 获取操作的错误码.
 * @param handle 解码器句柄，NULL 表示全局错误码
 * @return 0 表示没有错误
 */
DLLEXPORT int WINAPI MDecode_GetLastError(HANDLE handle);


/**
 * 获取解码器句柄.
 * @return 解码器句柄， NULL表示失败， 需要调用MDecode_DestroyPlayer进行关闭
 */
DLLEXPORT HANDLE WINAPI MDecode_CreateHandle();

/**
 * 销毁解码器句柄.
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_DestroyHandle(HANDLE handle);


/**
 * 打开流式解码器.
 * @param handle 解码器句柄
 * @param buffer 缓冲区指针
 * @param length 缓冲区长度
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_OpenStream(HANDLE handle, const MDecode_Format* fmt);


/**
 * 关闭流式解码器.
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_CloseStream(HANDLE handle);


/**
 * 输入视频数据.
 * @param handle 解码器句柄
 * @param mediaType 数据类型，比如视频，音频，复合流
 * @param buffer 数据缓冲区
 * @param length 数据长度
 * @param pts	 时间戳, 单位为微秒
 * @param flags  关键帧标识等, @see MDecode_MediaFlag
 * @return TRUE 表示解码成功
 */
DLLEXPORT BOOL WINAPI MDecode_InputData(HANDLE handle, int mediaType, const uint8_t* buffer, int length, int64_t pts, int flags);


/**
 * 重置输入缓冲区.
 * @param handle
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_ResetBuffer(HANDLE handle);


/**
 * 设置显示窗口.
 * @param handle 解码器句柄
 * @param hwnd  窗口句柄
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetVideoWnd(HANDLE handle, HWND hwnd);



/**
 * 获取视频分辨率，调用MDecode_InputData解码成功之后才会返回有效值.
 * @param pWidth 宽
 * @param pHeight 高
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetPictureSize(HANDLE handle, int* pWidth, int* pHeight);

/**
 * 抓图，格式为BMP
 * @param handle 解码器句柄
 * @param filename 文件名
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Snap(HANDLE handle, const char* filename);

/**
 * 刷新窗口
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_Refresh(HANDLE handle);

/**
 * 播放
 * @param handle
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Play(HANDLE handle);

/**
 * 暂停
 * @param handle
 * @param pauseIt TRUE 表示暂停，FALSE表示恢复
 * @param TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Pause(HANDLE handle, BOOL pauseIt);

/**
 * 停止
 * @param handle 解码器句柄
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Stop(HANDLE handle);


/**
 * 解码回调
 * @param handle 解码器句柄
 * @param pPicture 图像对象指针
 * @param pUser 设置回调时传入的环境指针
 */
typedef void (* MediaDecodeCallback)(HANDLE handle, MDecode_Frame* frame, void* pUser);

/**
 * 显示回调
 * @param handle 解码器句柄
 * @param hdc 绘图设备环境句柄
 * @param pUser 设置回调时环境指针
 *
 */
typedef void (* MediaDrawCallback)(HANDLE handle, HDC hdc, HWND hwnd, void* pUser);

/**
 * 设置解码回调
 * @param handle 解码句柄
 * @param cb 回调函数
 * @param pUser 环境指针
 * @return TRUE 表示成功
 *
 */
DLLEXPORT BOOL WINAPI MDecode_SetDecodeCallback(HANDLE handle, MDecode_PixelFormat pixelFormat, MediaDecodeCallback cb, void* pUser);

/**
 * 设置显示回调，可在回调中进行绘图操作
 * @param handle 解码器句柄
 * @param cb 回调函数
 * @param pUser 环境指针
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetDrawCallback(HANDLE handle, MediaDrawCallback cb, void* pUser);




/**
 * 打开文件
 * @param filename 文件名
 * @return 解码器句柄， 需要调用MDecode_CloseFile关闭
 */
DLLEXPORT BOOL WINAPI MDecode_OpenFile(HANDLE handle, const char* filename);


/**
 * 关闭文件解码器
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_CloseFile(HANDLE handle);


/**
 * 打开文件
 * @param url 文件名或者网络URL
 * @param params 参数, 可以为NULL或者空
 * @return 解码器句柄， 需要调用 MDecode_CloseFile 关闭
 */
DLLEXPORT BOOL WINAPI MDecode_OpenFileEx(HANDLE handle, const char* url, const char* params);



/**
 * 获取播放进度
 * @param handle
 * @return 播放进度，范围为[0, 1]
 */
DLLEXPORT double WINAPI MDecode_GetPos(HANDLE handle);

/**
 * 设置播放文件，即定位操作seek
 * @param handle 解码器句柄
 * @param pos 进度值，范围为[0, 1]
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetPos(HANDLE handle, double pos);

/**
 * 获取文件的总时长,
 * @param handle 解码器句柄
 * @param pSeconds 总时长，单位为秒，有些文件无法获取时长
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetDuration(HANDLE handle, double* pSeconds);


/**
 * 设置播放速度
 * @param handle 解码器句柄
 * @param scale 播放速度，范围为[1/8, 8], 1表示正常速度，8 表示快八倍速，1/8表示慢八倍速
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetScale(HANDLE handle, double scale);


/**
 * 获取播放速度
 * @param handle
 * @param pScale
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetScale(HANDLE handle, double* pScale);


/**
 * 设置文件播放结束通知消息，消息的wparam表示句柄，lparam 事件类型
 * @param handle 解码器句柄
 * @param hwnd 消息窗口句柄
 * @param msg 消息类型
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetFileEndMsg(HANDLE handle, HWND hwnd, uint32_t msg);



/**
 * 打开音频
 * @param handle
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_OpenSound(HANDLE    handle);

/**
 * 关闭音频
 * @param handle 解码器句柄
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_CloseSound(HANDLE   handle);

/**
* 是否打开音频
* @param handle 解码器句柄
* @return TRUE 表示已打开
*/
DLLEXPORT BOOL  WINAPI MDecode_IsSoundOpen(HANDLE handle);

/**
 * 设置音频音量
 * @param handle 解码器句柄
 * @param volume 音量，范围为：[0-100], 0 表示静音
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_SetVolume(HANDLE    handle, int  volume);


/**
 * 获取音量
 * @param handle
 * @param pVolume 音量
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_GetVolume(HANDLE    handle, int*  pVolume);


/**
 * 获取当前播放的时间
 * @handle 解码库句柄
 * @pts  时间，单位为毫秒
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetTime(HANDLE handle, int64_t* pts);

/**
 * 定位到指定时间点
 * @param handle	解码库句柄
 * @param pts	时间, 单位为毫秒
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Seek(HANDLE handle, int64_t pts);


/**
 * 设置显示比例
 * 如果cx,cy= 0, 保持视频的原始比率
 * @param player
 * @param cx	宽度比例
 * @param cy	高度比例
 * @return
 */
DLLEXPORT BOOL WINAPI MDecode_SetRatio(HANDLE handle, int cx, int cy);


/**
 * 事件回调句柄
 * @param player	播放器句柄
 * @param event		事件类型
 * @param value		事件附加值
 * @param size		附加值长度
 * @param pUser	回调环境.
 */
typedef void (*DecoderEventCallback)(HANDLE handle, int event, const char* value, int size, void* pUser);


/**
 * 设置事件回调句柄.
 * @param player	播放器.
 * @param cb		回调函数.
 * @param pUser		回调环境.
 * @return
 */
DLLEXPORT BOOL MDecode_SetEventCallback(HANDLE handle, DecoderEventCallback cb, void* pUser);

/**
 * 设置视频过滤器.
 * @param filterString	过滤器字符串, ffmpeg规则.
 * @return TRUE 表示成功.
 */
DLLEXPORT BOOL MDecode_SetVideoFilter(HANDLE handle, const char* filterString);

/**
* 启用/禁止硬件加速.
* 默认是没有启用的.
* @param enable > 0 表示启用, 否则禁用.
* @return 
*/
DLLEXPORT void MDecode_EnableHwAccel(BOOL enabled);

/**
 * 开启录像.
 * 已经录像时, 再调用 StartRecord 会切换录像.
 * @param handle    播放器句柄
 * @param filepath  录像文件
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL MDecode_StartRecord(HANDLE handle, const char* filepath);

/**
 * 停止录像
 * @param handle
 * @return
 */
DLLEXPORT BOOL MDecode_StopRecord(HANDLE handle);

/**
 * 是否在录像中.
 * @param handle
 * @return TRUE 表示在录像.
 */
DLLEXPORT BOOL MDecode_IsRecording(HANDLE handle);



/**
* 创建录像
* @param filename 文件名
* @param fmt		媒体格式
* @return 录像句柄， NULL表示失败
*/
DLLEXPORT HANDLE WINAPI MDecode_OpenRecord(const char* filename, const MDecode_Format* fmt);


/**
* 写入媒体流
* @param handle 录像句柄
* @param buffer 数据指针
* @param length    数据长度
* @param mediaType  数据类型
* @return TRUE 表示成功
*/
DLLEXPORT BOOL WINAPI MDecode_WriteRecord(HANDLE handle, int mediaType,
	const uint8_t* buffer, int length, int64_t pts, int flags);


/**
* 关闭录像
* @param handle 录像句柄
*
*/
DLLEXPORT void WINAPI MDecode_CloseRecord(HANDLE handle);

/**
* 创建录像
* @param filename 文件名
* @param fmt		媒体格式
* @param codec		输入流格式, 比如 MDECODE_CODEC_PS
* @return 录像句柄， NULL表示失败
*/
DLLEXPORT HANDLE WINAPI MDecode_OpenRecordEx(const char* filename, const MDecode_Format* fmt, int codec);


/**
 * 延时指定时长后播放.
 * @param handle
 * @param milliseconds 延时长度, 单位为毫秒.
 * @return TRUE 表示成功.
 */
DLLEXPORT BOOL WINAPI MDecode_SetDelayTime(HANDLE handle, int milliseconds);


/**
* 修改音频格式.
* @param handle
* @param codec		音频编码
* @param channels	通道数
* @param samplerate	采样率
* @param config		配置数据指针, 可以为NULL
* @param configSize	配置数据长度, 可以为0
* @return TRUE 表示成功.
*/
DLLEXPORT BOOL WINAPI MDecode_SetAudioFormat(HANDLE handle, int codec, int channels, int samplerate, const uint8_t* config, int configSize);

/**
 * 设置参数
 * @param handle    解码句柄
 * @param name      名称
 * @param value     值
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetParam(HANDLE handle, const char* name, const char* value);


/**
 * 设置解码回调频率.
 * @param handle        解码句柄
 * @param freq  频率, <= 0 表示始终调用解码, >= 1 表示每多少帧调用一次.
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetDecodeCallbackFreq(HANDLE handle, int freq);

/**
* 设置人脸轨迹显示时长, 单位为毫秒. 即 单个轨迹显示多长时间后消失
* @param ms	显示时长
* @return 
*/
DLLEXPORT void WINAPI MDecode_SetFaceTrackDuration(int ms);



/**
 * 运行/禁止鼠标绘制图形
 * @param mode	绘制模式, @see MDecode_DrawGraphMode.
 */
DLLEXPORT void WINAPI MDecode_EnableDrawGraph(HANDLE handle, int mode);

/**
 * 设置绘制图形, JSON 格式.
 * 实例:
 * {
 * "width": 1280, "height": 720,
 * "include": {"left": 2, "top": 2, "right": 200, "bottom": "200"},
 * "exclude": [{"x": 300, "y": 300},{"x": 400, "y": 400},{"x": 400, "y": 300}, {"x": 300, "y": 300}]
 * }
 *
 * 其中, include 表示感兴趣区域, exclude 表示排斥的多边形, 封闭的(首位坐标相同). width, height 表示分辨率.
 * @param value	JSON字符串, 为NULL或者空表示清理图形
 * @param size
 */
DLLEXPORT void WINAPI MDecode_SetDrawGraph(HANDLE handle, const char* value, size_t size);

/**
 * 获取绘制图形, JSON 格式
 * @return
 */
DLLEXPORT const char* WINAPI MDecode_GetDrawGraph(HANDLE handle);

/**
* 删除指定图形
* @param handle
* @param mode	指定图形, @see MDecode_DrawGraphMode. 如果是 MDECODE_DRAW_GRAPH_NONE, 则全部删除.
*/
DLLEXPORT void WINAPI MDecode_RemoveDrawGraph(HANDLE handle, int mode);


/**
 * 设置全屏
 * @param handle
 * @param isFull	是否全屏
 */
DLLEXPORT void WINAPI MDecode_SetFullscreen(HANDLE handle, BOOL isFull);

/**
 * 判断是否全屏
 * @param handle
 * @return TRUE 表示全屏
 */
DLLEXPORT BOOL WINAPI MDecode_IsFullscreen(HANDLE handle);



/////////////////////////////////////////////////////////////////////////////

#ifdef __cplusplus
}
#endif

#endif /* LIBMEDIADECODER_H_ */
